﻿
Imports System.Drawing
Imports std

Namespace graphUtils
    Public Module graphUtils
        Private Function minmax(ByVal dots As vector(Of pair(Of Int64, Int64)), ByVal steps As Int64, _
                                ByRef minwidth As Int64, ByRef maxwidth As Int64, _
                                ByRef minheight As Int64, ByRef maxheight As Int64) As Boolean
            If dots Is Nothing OrElse dots.size() = 0 Then
                Return False
            End If

            Dim i As Int64
            maxwidth = minInt64
            minwidth = maxInt64
            maxheight = minInt64
            minheight = maxInt64

            If steps <= 0 Then
                steps = 1
            End If
            For i = 0 To dots.size() - 1 Step steps
                If dots(i).first > maxwidth Then
                    maxwidth = dots(i).first
                End If
                If dots(i).first < minwidth Then
                    minwidth = dots(i).first
                End If
                If dots(i).second > maxheight Then
                    maxheight = dots(i).second
                End If
                If dots(i).second < minheight Then
                    minheight = dots(i).second
                End If
            Next

            Return True
        End Function

#If RETIRED Then
        Private Function minmax(Of XT)(ByVal dots As vector(Of pair(Of XT, Int64)), _
                                       ByRef minheight As Int64, ByRef maxheight As Int64) As Boolean
            If dots Is Nothing OrElse dots.size() = 0 Then
                Return False
            End If
            maxheight = minInt64
            minheight = maxInt64

            Dim i As Int64
            For i = 0 To dots.size() - 1
                If dots(i).second > maxheight Then
                    maxheight = dots(i).second
                End If
                If dots(i).second < minheight Then
                    minheight = dots(i).second
                End If
            Next

            Return True
        End Function
#End If

        Private Function normalize(ByVal i As Int64, _
                                   ByVal max As Int64, ByVal min As Int64, _
                                   ByVal size As Int64) As Int64
            Const bound As Byte = 8

            If max = min Then
                Return bound
            Else
                Return (i - min) / (max - min) * (size - (bound << 1)) + bound
            End If
        End Function

#If RETIRED Then
        Private Function normalize(ByVal index As Int64, ByVal dotssize As Int64, ByVal size As Int64) As Int64
            Const bound As Byte = 8

            Return (index \ dotssize) * (size - (bound << 1)) + bound
        End Function
#End If

        Private Function normalize(ByVal dots As vector(Of pair(Of Int64, Int64)), _
                                   ByVal width As Int64, ByVal height As Int64, _
                                   ByVal steps As Int64) As vector(Of pair(Of Int64, Int64))
            If dots Is Nothing OrElse dots.size() = 0 Then
                Return Nothing
            End If

            Dim rtn As vector(Of pair(Of Int64, Int64)) = Nothing
            rtn = New vector(Of pair(Of Int64, Int64))()
            Dim maxwidth As Int64
            Dim minwidth As Int64
            Dim maxheight As Int64
            Dim minheight As Int64
            If Not minmax(dots, steps, minwidth, maxwidth, minheight, maxheight) Then
                Return Nothing
            End If

            If steps <= 0 Then
                steps = 1
            End If
            Dim i As Int64
            For i = 0 To dots.size() - 1
                Dim p As pair(Of Int64, Int64) = Nothing
                If i Mod steps = 0 Then
                    p = make_pair(normalize(dots(i).first, maxwidth, minwidth, width), _
                                  height - normalize(dots(i).second, maxheight, minheight, height))
                Else
                    p = make_pair(minInt64, minInt64)
                End If
                rtn.push_back(p)
            Next

            Return rtn
        End Function

#If RETIRED Then
        Private Function normalize(Of XT)(ByVal dots As vector(Of pair(Of XT, Int64)), _
                                          ByVal width As Int64, _
                                          ByVal height As Int64) As vector(Of pair(Of Int64, Int64))
            If dots Is Nothing OrElse dots.size() = 0 Then
                Return Nothing
            End If

            Dim rtn As vector(Of pair(Of Int64, Int64)) = Nothing
            rtn = New vector(Of pair(Of Int64, Int64))()
            Dim maxheight As Int64
            Dim minheight As Int64
            If Not minmax(dots, minheight, maxheight) Then
                Return Nothing
            End If

            Dim i As Int64
            For i = 0 To dots.size() - 1
                Dim p As pair(Of Int64, Int64) = Nothing
                p = make_pair(normalize(i, dots.size(), width), _
                              normalize(dots(i).second, maxheight, minheight, height))
                rtn.push_back(p)
            Next

            Return rtn
        End Function
#End If

        Public Function drawLineChart(ByRef bmp As Bitmap, ByVal dots As vector(Of pair(Of Int64, Int64)), _
                                      Optional ByRef drawdots As vector(Of pair(Of Int64, Int64)) = Nothing, _
                                      Optional ByVal width As Int64 = 640, _
                                      Optional ByVal height As Int64 = 480, _
                                      Optional ByVal maxcount As Int64 = 32, _
                                      Optional ByRef pencircle As Pen = Nothing, _
                                      Optional ByRef penline As Pen = Nothing, _
                                      Optional ByRef brushtext As Brush = Nothing) As Boolean
            On Error Resume Next
            Const circleSize As Byte = 4
            Const lineSize As Byte = 2
            Const textSize As Byte = 2
            Const textEm As Byte = 12

            If dots Is Nothing OrElse dots.size() = 0 OrElse width <= 0 OrElse height <= 0 Then
                raiseError("dots is nothing or width / height <=0", errorHandle.errorType.warning)
                Return False
            End If

            Dim steps As Int64
            steps = dots.size() \ maxcount
            If steps <= 0 Then
                steps = 1
            End If
            If Not drawdots Is Nothing Then
                drawdots.clear()
            End If
            drawdots = normalize(dots, width, height, steps)
            If drawdots Is Nothing OrElse drawdots.size() <> dots.size() Then
                raiseError("cannot normalize dots.", errorHandle.errorType.warning)
                Return False
            End If

            If bmp Is Nothing Then
#If PocketPC OrElse Smartphone Then
                bmp = New Bitmap(width, height, Imaging.PixelFormat.Format32bppRgb)
#Else
                bmp = New Bitmap(width, height, Imaging.PixelFormat.Format32bppArgb)
#End If
            End If

            Dim grap As Graphics = Nothing
            grap = Graphics.FromImage(bmp)
            If grap Is Nothing Then
                Return False
            End If
#If Not (PocketPC OrElse Smartphone) Then
            grap.TextRenderingHint = Text.TextRenderingHint.SystemDefault
#End If

            'Dim pencircle As Pen = Nothing
            pencircle = New Pen(rndcolor(), circleSize)
            'Dim penline As Pen = Nothing
            Dim penlineColor As Color = rndcolor()
            penline = New Pen(penlineColor, lineSize)
            'Dim brushtext As Brush = Nothing
            brushtext = New SolidBrush(penlineColor)
            Dim font As Font = Nothing
#If PocketPC OrElse Smartphone Then
            font = New Font(Drawing.FontFamily.GenericMonospace, textEm, FontStyle.Regular)
#Else
            font = New Font(Drawing.FontFamily.GenericMonospace, textEm, FontStyle.Regular, GraphicsUnit.Pixel)
#End If

            Dim i As Int64
            For i = 0 To drawdots.size() - 1 Step steps
                grap.DrawEllipse(pencircle, drawdots(i).first, drawdots(i).second, circleSize, circleSize)
                Dim y As Int64
                If (drawdots(i).second < (height >> 1)) Then
                    y = drawdots(i).second + textEm + textSize
                Else
                    y = drawdots(i).second - textEm - textSize
                End If
                grap.DrawString(Convert.ToString(dots(i).first) + ", " + Convert.ToString(dots(i).second), _
                                font, brushtext, drawdots(i).first - textEm - textSize, y)

                If i > 0 Then
                    grap.DrawLine(penline, _
                                  drawdots(i - steps).first + lineSize, drawdots(i - steps).second + lineSize, _
                                  drawdots(i).first + lineSize, drawdots(i).second + lineSize)
                End If
            Next

            grap.Dispose()
            Return True
        End Function
    End Module
End Namespace
